package org.tau.workshop2011.semantics

import scala.collection.mutable.{ Set => MutableSet, HashSet => MutableHashSet }
import org.tau.workshop2011.parsing.AST._
import org.tau.workshop2011.expressions.Type
import java.lang.AssertionError

object TypeResolver {

  def resolveTypes(wa: SemanticAnalysis.WidgetAnalysis) {
    val widget: Widget = wa.rootWidget

    val dependencies = DependencyResolver.resolveDependencies(widget)
    /* If any cyclic dependency was present, it would have been already
     * detected by the symbol resolver or the dependency checker. Reaching
     * this line means we have no cyclic dependencies.
     *
     * So now, to resolve the types of all the variables, we will keep a set V
     * of variables whose type was already resolved (the initial value of V
     * would be the empty set)*. We will then iterate over all the variables
     * whose type is still unresolved, and will try to resolve the types of
     * variables that all their dependencies are in V**. After that, we add
     * the variable we just finished working on to V.
     *
     * The process is guaranteed to terminate since we have no cyclic
     * dependencies!
     *
     * *,** : The types of some variables may actually be inferred only due to
     * usage in their dependants (variables that depend on them). So V won't
     * hold only variables whose type was resolved, but also variables that we
     * tried to resolve their type (after doing so for all their dependencies)
     * and failed
     */
    val allVars = dependencies.dependencySortedVars
    val V:MutableSet[ResolvedVariable] = new MutableHashSet

    /* First, resolve the tyoe according to widget attributes */
    widget accept new ASTDeepVisitor {
      override def visit(node: AtomicWidget): Any = {
        node.autoResolveAttributes()
      }
    }

    /* Then try to infer everything else, possibly using the information gained
     * in the previous step */
    for (variable <- allVars) {
      if(V contains variable)
        throw new AssertionError("The variable " + variable + " was repeated twice")
      if (! (dependencies(variable) subsetOf V))
        throw new AssertionError("The dependencies of " + variable + " were not resolved before it! It depends on " + dependencies(variable))
      variable.definition.autoResolveType
      V += variable
    }

    /* Now, we want to check if we managed to figure out the type of all the
     * variables. If not, we must throw an exception */
    for (variable <- allVars)
      if (variable.definition.getLeftType == Type.tUnknown)
        throw new Exception ("Can't figure out the type of the variable " + variable)
  }
}